package com.starsea.book.ui.book.toc


import android.app.Application
import android.net.Uri
import androidx.lifecycle.MutableLiveData
import com.starsea.book.R
import com.starsea.book.base.BaseViewModel
import com.starsea.book.constant.AppConst
import com.starsea.book.constant.AppLog
import com.starsea.book.data.appDb
import com.starsea.book.data.entities.Book
import com.starsea.book.data.entities.BookChapter
import com.starsea.book.exception.NoStackTraceException
import com.starsea.book.help.book.BookHelp
import com.starsea.book.help.book.isLocal
import com.starsea.book.help.http.newCallResponseBody
import com.starsea.book.help.http.okHttpClient
import com.starsea.book.help.http.text
import com.starsea.book.model.ReadBook
import com.starsea.book.model.localBook.LocalBook
import com.starsea.book.ui.newpage.entity.BaseEntity
import com.starsea.book.ui.newpage.entity.BookSectionListEntity
import com.starsea.book.ui.newpage.utils.gsonList
import com.starsea.book.utils.FileDoc
import com.starsea.book.utils.GSON
import com.starsea.book.utils.LogUtils
import com.starsea.book.utils.createFileIfNotExist
import com.starsea.book.utils.openOutputStream
import com.starsea.book.utils.toastOnUi
import com.starsea.book.utils.writeText
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.FileNotFoundException

class TocViewModel(application: Application) : BaseViewModel(application) {
    var bookUrl: String = ""
    var bookData = MutableLiveData<Book>()
    var chapterListCallBack: ChapterListCallBack? = null
    var bookMarkCallBack: BookmarkCallBack? = null
    var searchKey: String? = null

    fun initBook(bookUrl: String) {
        LogUtils.e(">>>> toc ", "toc : bookUrl = " + bookUrl)
        this.bookUrl = bookUrl
        execute {
            appDb.bookDao.getBook(bookUrl)?.let {
                bookData.postValue(it)
            }
        }
    }

    val permissionDenialLiveData = MutableLiveData<Int>()
    val loadChapterResultData = MutableLiveData<Boolean>()

    /**
     * 加载目录
     */
    fun loadChapterList(book: Book?) {

        book ?: return

        if (book.isLocal) {
            execute {
                LocalBook.getChapterList(book).let {
                    book.latestChapterTime = System.currentTimeMillis()
                    appDb.bookChapterDao.delByBook(book.bookUrl)
                    appDb.bookChapterDao.insert(*it.toTypedArray())
                    appDb.bookDao.update(book)
                    ReadBook.chapterSize = it.size
                    ReadBook.upMsg(null)
                    ReadBook.loadContent(resetPageOffset = true)
                }
            }.onError {
                when (it) {
                    is SecurityException, is FileNotFoundException -> {
                        permissionDenialLiveData.postValue(1)
                    }

                    else -> {
                        AppLog.put("LoadTocError:${it.localizedMessage}", it)
                        ReadBook.upMsg("LoadTocError:${it.localizedMessage}")
                    }
                }
            }
        } else {
            execute {
                okHttpClient.newCallResponseBody {
                    url(AppConst.BASE_URL.plus("front-api/book/book_section_list?bookId=${book.bookUrl}"))
                        .post("".toRequestBody())
                }.text().let {
                    LogUtils.e(">>>>> 章节列表", it)
                    val entity: BaseEntity<MutableList<BookSectionListEntity>> =
                        it.gsonList(BookSectionListEntity::class.java)

                    val chapterList: MutableList<BookChapter> = arrayListOf()
                    entity.data!!.forEachIndexed { index, bookSectionListEntity ->
                        val bookChapter = BookChapter()
                        bookChapter.title = bookSectionListEntity.sectionTitle.toString()
                        bookChapter.url = bookSectionListEntity.sectionId.toString()
//                    是否免费 0:免费 1:收费
//                        bookChapter.isVip = bookSectionListEntity.isFree == 0
                        bookChapter.isPay = bookSectionListEntity.isFree == 0
                        bookChapter.bookUrl = book.bookUrl
                        bookChapter.index = index
                        chapterList.add(bookChapter)
                    }
                    val oldBook = book.copy()
                    if (oldBook.bookUrl == book.bookUrl) {
                        appDb.bookDao.update(book)
                    } else {
                        appDb.bookDao.insert(book)
                        BookHelp.updateCacheFolder(oldBook, book)
                    }
                    appDb.bookChapterDao.delByBook(oldBook.bookUrl)
                    appDb.bookChapterDao.insert(*chapterList.toTypedArray())
                    loadChapterResultData.postValue(true)

                }
            }.onError {
                context.toastOnUi(it.localizedMessage ?: "ERROR")
            }

        }
    }

    fun upBookTocRule(book: Book, finally: () -> Unit) {
        execute {
            appDb.bookDao.update(book)
            LocalBook.getChapterList(book).let {
                book.latestChapterTime = System.currentTimeMillis()
                appDb.bookChapterDao.delByBook(book.bookUrl)
                appDb.bookChapterDao.insert(*it.toTypedArray())
                appDb.bookDao.update(book)
                bookData.postValue(book)
            }
        }.onFinally {
            finally.invoke()
        }
    }

    fun reverseToc(success: (book: Book) -> Unit) {
        execute {
            bookData.value?.apply {
                setReverseToc(!getReverseToc())
                val toc = appDb.bookChapterDao.getChapterList(bookUrl)
                val newToc = toc.reversed()
                newToc.forEachIndexed { index, bookChapter ->
                    bookChapter.index = index
                }
                appDb.bookChapterDao.insert(*newToc.toTypedArray())
            }
        }.onSuccess {
            it?.let(success)
        }
    }

    fun startChapterListSearch(newText: String?) {
        chapterListCallBack?.upChapterList(newText)
    }

    fun startBookmarkSearch(newText: String?) {
        bookMarkCallBack?.upBookmark(newText)
    }

    fun saveBookmark(treeUri: Uri) {
        execute {
            val book = bookData.value
                ?: throw NoStackTraceException(context.getString(R.string.no_book))
            val fileName = "bookmark-${book.name} ${book.author}.json"
            val doc = FileDoc.fromUri(treeUri, true)
            doc.createFileIfNotExist(fileName).writeText(
                GSON.toJson(
                    appDb.bookmarkDao.getByBook(book.name, book.author)
                )
            )
        }.onError {
            AppLog.put("导出失败\n${it.localizedMessage}", it, true)
        }.onSuccess {
            context.toastOnUi("导出成功")
        }
    }

    fun saveBookmarkMd(treeUri: Uri) {
        execute {
            val book = bookData.value
                ?: throw NoStackTraceException(context.getString(R.string.no_book))
            val fileName = "bookmark-${book.name} ${book.author}.md"
            val treeDoc = FileDoc.fromUri(treeUri, true)
            val fileDoc = treeDoc.createFileIfNotExist(fileName)
                .openOutputStream()
                .getOrThrow()
            fileDoc.use { outputStream ->
                outputStream.write("## ${book.name} ${book.author}\n\n".toByteArray())
                appDb.bookmarkDao.getByBook(book.name, book.author).forEach {
                    outputStream.write("#### ${it.chapterName}\n\n".toByteArray())
                    outputStream.write("###### 原文\n ${it.bookText}\n\n".toByteArray())
                    outputStream.write("###### 摘要\n ${it.content}\n\n".toByteArray())
                }
            }
        }.onError {
            AppLog.put("导出失败\n${it.localizedMessage}", it, true)
        }.onSuccess {
            context.toastOnUi("导出成功")
        }
    }

    interface ChapterListCallBack {
        fun upChapterList(searchKey: String?)

        fun clearDisplayTitle()
    }

    interface BookmarkCallBack {
        fun upBookmark(searchKey: String?)
    }
}